package com.baijiayun.sdk.core;

import com.baijiayun.sdk.exception.ApiException;
import com.baijiayun.sdk.model.BaseRequest;
import com.baijiayun.sdk.util.RequestParamsUtils;
import com.baijiayun.sdk.util.SignUtils;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @param <I>
 * @author Smile
 */
public class JsonProxy<I> implements InvocationHandler {
    protected static final Logger logger = LoggerFactory.getLogger(JsonProxy.class);

    private Class<I> interfaces;
    private CommonService service;

    /**
     * @param interfaces
     * @param service
     */
    public JsonProxy(Class<I> interfaces, CommonService service) {
        super();
        this.interfaces = interfaces;
        this.service = service;
    }

    public static <T> T createProxy(Class<T> interfaces, CommonService service) throws ApiException {
        JsonProxy<T> proxy = new JsonProxy<T>(interfaces, service);
        service.generateHeader();
        return (T) Proxy.newProxyInstance(JsonProxy.class.getClassLoader() , new Class<?>[]{interfaces},
                proxy);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String url = "http://" + service.domainName + AddressUtil.getJsonAddr(interfaces) + "/" + method.getName();
        RestTemplate restTemplate = new RestTemplate();
        //设置请求头(注意会产生中文乱码)
        FormHttpMessageConverter fc = new FormHttpMessageConverter();
        StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        List<HttpMessageConverter<?>> partConverters = new ArrayList<HttpMessageConverter<?>>();
        partConverters.add(stringConverter);
        partConverters.add(new ResourceHttpMessageConverter());
        fc.setPartConverters(partConverters);
        restTemplate.getMessageConverters().addAll(Arrays.asList(fc, new MappingJackson2HttpMessageConverter()));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(makeRequest(args[0]), httpHeaders);
        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .setPrettyPrinting()
                .create();
        // 返回json字符串
        String responseStr = restTemplate.postForObject(url, request, String.class);
        AnnotatedType annotatedType = method.getAnnotatedReturnType();
        method.getAnnotatedReturnType();
        Type objectType = null;
        if (annotatedType.getType() instanceof ParameterizedType) {
            // 获取方法返回类型
            ParameterizedType type = (ParameterizedType) method.getAnnotatedReturnType().getType();
            objectType = type(Msg.class, type.getActualTypeArguments()[0]);
        } else {
            objectType = annotatedType.getType();
        }
        return gson.fromJson(responseStr, objectType);
    }

    /**
     * 生成请求参数
     *
     * @param obj
     * @return
     */
    protected MultiValueMap<String, Object> makeRequest(Object obj) throws IllegalAccessException {
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
        if (obj instanceof BaseRequest) {
            BaseRequest baseRequest = (BaseRequest) obj;
            baseRequest.setPartnerId(service.getPartnerId());
            baseRequest.setTimestamp(System.currentTimeMillis());
            baseRequest.setSign(SignUtils.getSign(obj, service.partnerKey));
        }
        RequestParamsUtils.pushFieldToMap(obj, map);
        return map;
    }

    /**
     *
     * @param raw
     * @param args
     * @return
     */
    private ParameterizedType type(final Class raw, final Type... args) {
        return new ParameterizedType() {
            @Override
            public Type getRawType() {
                return raw;
            }
            @Override
            public Type[] getActualTypeArguments() {
                return args;
            }
            @Override
            public Type getOwnerType() {
                return null;
            }
        };
    }
}
